/*
 * By Luocheng Tang
 * dynamic configure master/slave data source connection with user's
 * option
 */
package com.lvmama.vst.batis.config;

import static com.lvmama.vst.batis.datasource.DataSourceType.READ;
import static com.lvmama.vst.batis.datasource.DataSourceType.WRITE;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.lvmama.vst.batis.datasource.DynamicDataSource;
import com.lvmama.vst.batis.dsobject.DataSourceUtil;
import com.lvmama.vst.batis.dsobject.DsObject;

@Profile({"release", "production", "staging", "release-candidate", "Read_Write"})
@Configuration
@ComponentScan("com.lvmama.vst.batis")
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class DualDataSourceConfig {

	private static Logger logger = LoggerFactory.getLogger(DualDataSourceConfig.class);

	@Autowired
	@Qualifier("DsObjectMaster") 
	private DsObject dsObjectMaster;
	
	@Autowired
	@Qualifier("DsObjectSlave") 
	private DsObject dsObjectSlave;
	
	@Value("${spring.datasource.type: }") 
	private String dataSourceType;
	private Class<? extends DataSource> targetClass = null;

	// configure dynamic data source 
	@ConditionalOnMissingBean({ DataSource.class})
	@Bean(name = "dataSource")
	public DataSource dataSource() {
		logger.info("init dynamicDataSource");
		DynamicDataSource dataSource = new DynamicDataSource();

		Map<Object, Object> targetDataSources = new HashMap<>();
		targetDataSources.put(WRITE.name(), createDataSource(dsObjectMaster));
		targetDataSources.put(READ.name(), createDataSource(dsObjectSlave));
		dataSource.setTargetDataSources(targetDataSources);
		dataSource.setDefaultTargetDataSource(targetDataSources.get(WRITE.name()));
		return dataSource;
	}
	
	@Bean
    public PlatformTransactionManager annotationDrivenTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
	
	/*
	 * 	decide which DataSource to use, the order is 
	 *  1). user's input from spring.datasource.type field
	 *  2). if DruidDataSource is in classpath (pom.xml)
	 *  3). Tomcat is the last option chosen by Spring which is always available
	 *  
	*/
	private Class<? extends DataSource> getDataSourceClass() throws ClassNotFoundException {
    	
    	if (null != targetClass) {
    		return targetClass;
    	}
    	String[] sourceArray = {this.dataSourceType, 
				DataSourceUtil.DRUID_DATA_SOURCE, 
				DataSourceUtil.TOMCAT_DATA_SOURCE };
    	for (String sourceType : sourceArray) {
    		targetClass = DataSourceUtil.classInClasspath(sourceType);
    		if (null != targetClass) {
    			break;
    		}
    	}
    	return targetClass;
	}

	private DataSource createDataSource(DsObject dsObject) {
		logger.info("init createDataSource from " + dsObject.getClass().getName());
		try {
			return DataSourceUtil.objToDataSource(getDataSourceClass(), dsObject);
		} catch (Exception e) {
		}
		return null;
	}
}
